package com.biye.api.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.base.common.alipay.util.AlipayUtil;
import com.base.common.edu.entity.StuAccount;
import com.base.common.edu.entity.StuAccountLog;
import com.base.common.edu.entity.Student;
import com.base.common.edu.mapper.StuAccountLogMapper;
import com.base.common.edu.mapper.StuAccountMapper;
import com.base.common.edu.mapper.StudentMapper;
import com.base.common.edu.util.Dateutil;
import com.base.common.edu.util.JSONFilter;
import com.base.common.edu.util.Page;
import com.base.common.httpStatusCode.enumDefine.StatusCode;
import com.biye.api.service.MoneyService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
@Transactional
public class MoneyServiceImpl implements MoneyService {

    @Resource
    private StuAccountMapper stuAccountMapper;

    @Resource
    private StuAccountLogMapper stuAccountLogMapper;

    @Resource
    private StudentMapper studentMapper;

    @Override
    public JSONObject queryStuAccount(Map<String, Object> map) {
        JSONObject json = new JSONObject();
        int statusCode = StatusCode.Failure._statusCode;
        String msg = StatusCode.Failure.explain;

        // 判断请求参数
        if ( ! map.containsKey("u_id") ){
            json.put("statusCode", statusCode);
            json.put("msg", "请求参数有误");
            return json;
        }

        String u_id = (String) map.get("u_id");
        StuAccount sa = stuAccountMapper.queryStuAccountById(u_id);

        // 若该学生账号下未初始化余额账户，首先初始化账户
        if ( sa == null ){

            Student student = studentMapper.queryStudentById(u_id);
            if( student != null) {
                /***
                 * 如果学生账号存在，那么需要帮该学生初始化账户
                 */
                StuAccount init_stuAccount = new StuAccount();
                init_stuAccount.setStudentAccountId(u_id);
                BigDecimal bd=new BigDecimal("0.00");
                //设置小数位数，第一个变量是小数位数，第二个变量是取舍方法(四舍五入)
                bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
                init_stuAccount.setBalance(bd);
                int i = stuAccountMapper.addStuAccount(init_stuAccount);
                System.out.println(i > 0 ? "学生ID=" + u_id + "初始化账户成功" : "学生ID=" + u_id + "初始化账户失败" );
                // 重新赋值
                sa = stuAccountMapper.queryStuAccountById(u_id);
            }else{
                // 如果不存在该学生账号，那么不进行初始化账户操作
            }

        }

        // 此时若系统中有u_id对应的学生账户，那么它的账户现在是可以查出数据的，包括初始化后的0.00
        if ( sa != null ) {

            json.put("balance", sa.getBalance().setScale(2, BigDecimal.ROUND_HALF_UP).toString());
            statusCode = StatusCode.Success._statusCode;
            msg = "查询成功";

        }

        json.put("statusCode", statusCode);
        json.put("msg", msg);
        return json;
    }

    // 余额提现
    @Override
    public JSONObject withdraw(Map<String, Object> map) {
        JSONObject json = new JSONObject();
        int statusCode = StatusCode.Failure._statusCode;
        String msg = StatusCode.Failure.explain;

        // 判断请求参数
        if ( ! map.containsKey("u_id") ){
            json.put("statusCode", statusCode);
            json.put("msg", "请求参数有误");
            return json;
        }

        // 提现用户支付宝的登录邮箱号或手机号
        if ( ! map.containsKey("eamil_tel") ){
            json.put("statusCode", statusCode);
            json.put("msg", "收款人手机号或邮箱号未填写");
            return json;
        }
        // 提现用户的真人“认证姓名”
        if ( ! map.containsKey("realname") ){
            json.put("statusCode", statusCode);
            json.put("msg", "收款人真实姓名未填写");
            return json;
        }
        // 提现金额
        if ( ! map.containsKey("amount") ){
            json.put("statusCode", statusCode);
            json.put("msg", "收款人提现金额未填写");
            return json;
        }
        // 提现备注
        if ( ! map.containsKey("remark") ){
            json.put("statusCode", statusCode);
            json.put("msg", "收款人自定义备注未填写");
            return json;
        }

        // 校验账号合法性
        String u_id = (String) map.get("u_id");
        Student student = studentMapper.queryStudentById(u_id);
        if( student == null){
            json.put("statusCode", statusCode);
            json.put("msg", "请求参数有误");
            return json;
        }

        String amount = (String) map.get("amount");
        // 正式逻辑
        BigDecimal amount_bd = new BigDecimal(amount);
        amount_bd = amount_bd.setScale(2, BigDecimal.ROUND_HALF_UP);
        // 对提现金额进行判断  signum返回 的是 -1, 0, 1，分别表示 负数、零、正数
        int signum = amount_bd.signum();
        System.out.println(amount_bd + " " + signum);
        if (signum == -1){
            json.put("statusCode", statusCode);
            json.put("msg", "提现余额不为正数，非法操作");
            return json;
        }
        // 账户的余额 Balance
        StuAccount stuAccount = stuAccountMapper.queryStuAccountById(u_id);
        BigDecimal surplus_bd = stuAccount.getBalance();
        surplus_bd = surplus_bd.setScale(2, BigDecimal.ROUND_HALF_UP);

        /***
         * 判断该提现操作是否能够进行（判断账户余额surplus_bd是否大于提现金额amount_bd）
         * result = 1:  amount_bd 大于 surplus_bd
         * result = 0:  amount_bd 等于 surplus_bd
         * result = -1:  amount_bd 小于 surplus_bd
         */
        int result = amount_bd.compareTo(surplus_bd);

        if ( result <= 0 ){
            String eamil_tel = (String) map.get("eamil_tel");
            String realname = (String) map.get("realname");
            String remark = (String) map.get("remark");

            Map<String, String> stringStringMap = AlipayUtil.fundTransfer(amount, "教务系统用户提现", eamil_tel, 2, realname, remark, "Edu教务系统");
            boolean result_ = "true".equals(stringStringMap.get("status")) ? true : false;
            msg = stringStringMap.get("msg");
            if ( result_ ){
                statusCode = StatusCode.Success._statusCode;

                BigDecimal after_balance = surplus_bd.subtract(amount_bd);
                after_balance = after_balance.setScale(2, BigDecimal.ROUND_HALF_UP);

                // 只有两位小数的两个数相乘，小数位也是二位，不需要setScale() again
                StuAccount init_stuAccount = new StuAccount();
                init_stuAccount.setStudentAccountId(u_id);
                init_stuAccount.setBalance(after_balance);
                // 更新账户余额
                int i = stuAccountMapper.updateStuAccount(init_stuAccount);

                if ( i > 0 ) {
                    System.out.println(i > 0 ? "[提现]学生ID=" + u_id + "账户余额更新成功" : "[提现]学生ID=" + u_id + "账户余额更新失败" );
                    // 将充值记录存入Log表
                    StuAccountLog stuAccountLog = new StuAccountLog();
                    Student log_stu = new Student();
                    log_stu.setStuID(u_id);
                    stuAccountLog.setStudent(log_stu);
                    stuAccountLog.setAccType(4);
                    stuAccountLog.setAccExplain("学生个人账户余额提现====" + stringStringMap.toString());
                    stuAccountLog.setAmountPaid(amount_bd);
                    stuAccountLog.setAfterSurplus(after_balance);
                    int i1 = stuAccountLogMapper.addStuAccountLog(stuAccountLog);
                    System.out.println(i1 > 0 ? "[提现]学生ID=" + u_id + "账户余额更新记录插入成功" : "[提现]学生ID=" + u_id + "账户余额更新记录插入失败" );
                }

            }

        }else{
            msg = "提现金额不能大于账户余额";
        }

        json.put("statusCode", statusCode);
        json.put("msg", msg);
        return json;
    }

    // 学生账户余额修改：教材费付款   --- 暂未开放
    @Override
    public JSONObject textBookPay(Map<String, Object> map) {
        return null;
    }

    // 查询账户变更记录
    @Override
    public String queryLog(Map<String, Object> map) {
        JSONObject json = new JSONObject();
        int statusCode = StatusCode.Failure._statusCode;
        String msg = "暂无数据";
        if ( !map.containsKey("pageNum") || !map.containsKey("pageSize") || !map.containsKey("userId")
                || !map.containsKey("startTime")
                || !map.containsKey("endTime")
                || !map.containsKey("bizType")
        ){
            json.put("statusCode", statusCode);
            json.put("msg", "请求参数不正确");
            return json.toJSONString();
        }
        int pageNum, pageSize;
        String stu_id;
        try{
            pageNum = (int) map.get("pageNum");
            pageSize = (int) map.get("pageSize");
            stu_id = (String) map.get("userId");
        }catch (Exception e){
            json.put("statusCode", statusCode);
            json.put("msg", "参数格式不正确");
            return json.toJSONString();
        }
        // Time
        String startTime = (String) map.get("startTime");
        String endTime = (String) map.get("endTime");
        Date start_time = null;
        Date end_time = null;
        if ( !"".equals(startTime) ) {
            start_time = Dateutil.parseStringToDate(startTime, Dateutil.myDateutil);
            if ( start_time == null ){
                json.put("statusCode", statusCode);
                json.put("msg", "Time参数格式不正确");
                return json.toJSONString();
            }
        }
        if ( !"".equals(endTime) ) {
            end_time = Dateutil.parseStringToDate(endTime, Dateutil.myDateutil);
            if ( end_time == null ){
                json.put("statusCode", statusCode);
                json.put("msg", "Time参数格式不正确");
                return json.toJSONString();
            }
        }
        // bizType
        String bizType = (String) map.get("bizType");
        Integer biz_type = null;
        if ( "1".equals(bizType) || "2".equals(bizType) || "3".equals(bizType) || "4".equals(bizType) )
            biz_type = Integer.parseInt(bizType);
        // 由于PageHelper有太多的参数，但是他又能提供分页查询，所以利用这个特性查询到需要的数据，再自己封装对象
        PageHelper.startPage(pageNum, pageSize);
        List<StuAccountLog> users = stuAccountLogMapper.queryStuAccountLogByStuId(stu_id, start_time, end_time, biz_type);
        /*
            由于使用分页插件pageHelper的方法包含了级联查询的方法，即@Many或@One
            所以需要在该方法所涉及的所有实体类上标注@JsonIgnoreProperties(value = {"handler"})注解以解决序列化报错问题
//          return pageInfo; <= 主方法的返回值类型：Object
         */
        PageInfo<StuAccountLog> pageInfo = new PageInfo<>(users);
        // 由于想自定义分页参数，所以这边只把数据查出来，后边自己封装
        Page<StuAccountLog> p = new Page<StuAccountLog>(pageNum, pageSize, (int) pageInfo.getTotal());
        p.setList(users);
        if (users.size() > 0) {
            json.put("page", p);
            //过滤掉子元素中的数组
            JSONFilter[] jfs = {
                    // 可以排查当前类中的，如下
                    new JSONFilter(StuAccountLog.class, "student"),
                    // 如果当前类的子属性关联类需要用到一些值，但不是全部
//                    new JSONFilter(Student.class, "stuID", "sno", "sex", "password")
            };
            statusCode = StatusCode.Success._statusCode;
            msg = "数据查询成功";
            json.put("statusCode", statusCode);
            json.put("msg", msg);
            // 在这里，遇到了一个Java实体Date属性，转Json时变成timestamp的问题
            // FastJson(Ali)=>Solution is add "@JSONField(format = "yyyy-MM-dd HH:mm:ss")" annotation on entity's property top
            /*
            At the same time, I found a new problem related to time,
            database storage time and actual time difference of 8 hours.

            I fixed the time zone of the docker's internal machine, overloaded the configuration, and then the problem was fixed,

            The Linux instructions are as follows:
                docker exec -it aede28960ccd sh -c 'mkdir -p /usr/share/zoneinfo/Asia/'
                docker cp /usr/share/zoneinfo/Asia/Shanghai aede28960ccd:/usr/share/zoneinfo/Asia/Shanghai
                docker exec -it aede28960ccd sh -c 'ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime'
             */
            return JSONObject.toJSONString(json, jfs);
        }
        json.put("statusCode", statusCode);
        json.put("msg", msg);
        return json.toJSONString();
    }

    // 学生账户余额修改：教材费退款   --- 暂未开放
    @Override
    public JSONObject refund(Map<String, Object> map) {
        return null;
    }
}
